/*
 * Copyright (c) 2009, Jan Pastor. All Rights Reserved.
 */

package sk.jp.commons.utils;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Utility class for working with collections.
 * 
 * @author Jan Pastor 
 */
public class CollectionUtils {

    private CollectionUtils() {
    }

    // ----------------------------------------------------------------------

    /**
     * Create map of exception parameters from given variable list of parameter
     * names and values.
     * <p>
     * Values are transformed to string using <code>String.valueOf</code>
     * method.
     * @param params a list of parameter names and values (paramName0,
     *            paramValue0, paramName1, paramValue1, ...). Number of list
     *            items must be even (param name + param value). If null or
     *            empty, returns empty map.
     * @return ordered map of parameters (key: parameter name, value: parameter
     *         value).
     */
    public static LinkedHashMap paramsToMap(Object... params) {
        LinkedHashMap<Object, Object> out = new LinkedHashMap<Object, Object>();
        if (params == null || params.length == 0) {
            return out;
        }
        if (params.length % 2 > 0) {
            throw new IllegalArgumentException("Illegal number of parameters (" + params.length
                    + ")");
        }
        for (int i = 0; i < params.length; i++) {
            // noinspection AssignmentToForLoopParameter
            Object pName = params[i++];
            Object pValue = params[i];
            out.put(pName, String.valueOf(pValue));
        }
        return out;
    }

    /**
     * Creates a list of given items.
     * @param items items to store to list.
     * @return a list instance that contains given items.
     */
    public static <T> List<T> itemsToList(T... items) {
        if (items == null || items.length == 0) {
            return new ArrayList<T>(0);
        }
        return Arrays.asList(items);
    }

    /**
     * Creates a set from given items.
     * @param items - items to be stored in a set.
     * @return an instance of set containing given items or empty set if null or
     *         empty items specified.
     */
    public static <T> Set<T> itemsToSet(T... items) {
        if (items == null || items.length == 0) {
            return new HashSet<T>(0);
        }
        Set<T> set = new HashSet<T>();
        //noinspection ManualArrayToCollectionCopy
        for (T i : items) {
            set.add(i);
        }
        return set;
    }

    /**
     * Returns an (new) array filled by items from given list.
     * <p>
     * Example of usage:
     * 
     * <pre>
     * List&lt;IColumn&gt; columns = new ArrayList&lt;IColumn&gt;();
     * columns.add(new PropertyColumn());
     * IColumn[] columns = CollectionUtils.listToArray(columns, IColumn.class);
     * </pre>
     * 
     * @param list list to process. If null, returns null. If empty, result
     *            array will be empty.
     * @param clazz class to help create generic result array.
     * @return array of list items, or null if given list is null.
     */
    public static <T> T[] listToArray(List<T> list, Class clazz) {
        if (list == null) {
            return null;
        }
        // noinspection unchecked
        T[] array = (T[]) Array.newInstance(clazz, list.size());
        for (int i = 0; i < list.size(); i++) {
            T item = list.get(i);
            array[i] = item;
        }
        return array;
    }

    /**
     * Returns list items in text format, separated by specified separator.
     * @param list list of objects to print. If null or empty, returns empty
     *            string.
     * @param separator string used for separate list items. If null, uses
     *            default separator (", ").
     * @return list of list items in string, separated by separator.
     */
    public static String listToString(List list, String separator) {
        if (list == null || list.isEmpty()) {
            return "";
        } else if (list.size() == 1) {
            return String.valueOf(list.get(0));
        } else {
            if (separator == null) {
                separator = ", ";
            }
            StringBuilder buf = new StringBuilder();
            for (int i = 0; i < list.size(); i++) {
                Object o = list.get(i);
                if (i > 0) {
                    buf.append(separator);
                }
                buf.append(o);
            }
            return buf.toString();
        }
    }

    /**
     * Returns array items in text format, separated by specified separator.
     * @param array array of objects to print. If null or empty, returns empty
     *            string.
     * @param separator string used for separate array items. If null, uses
     *            default separator (", ").
     * @return array items in string, separated by separator.
     */
    public static String arrayToString(Object[] array, String separator) {
        if (array == null || array.length == 0) {
            return "";
        } else if (array.length == 1) {
            return String.valueOf(array[0]);
        } else {
            if (separator == null) {
                separator = ", ";
            }
            StringBuilder buf = new StringBuilder();
            for (int i = 0; i < array.length; i++) {
                Object o = array[i];
                if (i > 0) {
                    buf.append(separator);
                }
                if (o instanceof Object[]) {
                    buf.append('{');
                    buf.append(arrayToString((Object[]) o, separator));
                    buf.append('}');
                } else {
                    buf.append(o);
                }
            }
            return buf.toString();
        }
    }

    /**
     * Returns params in text format, separated by specified separator.
     * @param separator string used for separate params. If null, uses default
     *            separator (", ").
     * @param params variable list of objects to print. If null or empty,
     *            returns empty string.
     * @return params in string, separated by separator.
     */
    public static String paramsToString(String separator, Object... params) {
        return arrayToString(params, separator);
    }

    /**
     * Returns given map transformed to text form.
     * <p>
     * Entries are separated by given entry separator and values are aeparated
     * by given value separator.
     * <p>
     * If given map to transform is null, returns empty string.
     * <p>
     * Entry names (keys) and entry values are transformed to text form using
     * its <code>toString</code> method implementation.
     * @param map map to transform. If null, returns empty string.
     * @param valueSeparator value separator (between netry name (key) and
     *            value). If null, default separator is used.
     * @param entrySeparator entries separator (between map entries). If null,
     *            default separator is used.
     * @param quoteValue if true, values are quoted by ' character.
     * @return map content transformed to text form.
     */
    public static String mapToString(Map map, String valueSeparator, String entrySeparator,
            boolean quoteValue) {
        if (map == null || map.isEmpty()) {
            return "";
        } else {
            if (valueSeparator == null) {
                valueSeparator = ": ";
            }
            if (entrySeparator == null) {
                entrySeparator = ", ";
            }
            StringBuilder buf = new StringBuilder();
            Set entries = map.entrySet();
            int i = 0;
            for (Iterator iter = entries.iterator(); iter.hasNext();) {
                Map.Entry entry = (Map.Entry) iter.next();
                if (i++ > 0) {
                    buf.append(entrySeparator);
                }
                buf.append(entry.getKey());
                buf.append(valueSeparator);
                if (quoteValue) {
                    buf.append("'");
                }
                buf.append(entry.getValue());
                if (quoteValue) {
                    buf.append("'");
                }
            }
            return buf.toString();
        }
    }

    /**
     * Returns enumeration items in text format, separated by specified
     * separator.
     * <p>
     * This method is usefull for implementations of {@link Enumeration} which
     * doesn't support readable <code>toString</code> implementation.
     * @param e enumeration of objects to print. If null or empty, returns empty
     *            string.
     * @param separator string used for separate array items. If null, uses
     *            default separator (", ").
     * @return items in string, separated by separator.
     */
    public static String enumerationToString(Enumeration e, String separator) {
        if (e == null) {
            return "";
        } else {
            if (separator == null) {
                separator = ", ";
            }
            StringBuilder buf = new StringBuilder();
            int i = 0;
            while (e.hasMoreElements()) {
                Object o = e.nextElement();
                if (i > 0) {
                    buf.append(separator);
                }
                if (o instanceof Enumeration) {
                    buf.append('{');
                    buf.append(enumerationToString((Enumeration) o, separator));
                    buf.append('}');
                } else {
                    buf.append(o);
                }
                i++;
            }
            return buf.toString();
        }
    }

    /**
     * Checks if all values are null. If there are no input values (without
     * parameter), returns true.
     * @param values
     * @return true if all values are null.
     */
    public static boolean isAllNull(Object... values) {
        if (values == null) {
            return true;
        }
        for (Object value : values) {
            if (value != null) {
                return false;
            }
        }
        return true;
    }

    /**
     * Checks if all values are not null. If there are no input values (without
     * parameter), returns true.
     * @param values
     * @return true if all values are not null.
     */
    public static boolean isAllNotNull(Object... values) {
        if (values == null) {
            return false;
        }
        for (Object value : values) {
            if (value == null) {
                return false;
            }
        }
        return true;
    }

}
